﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Xml;
using eBdb.EpubReader;
using mshtml;
using System.IO;
//using CapstoneProjectAEReader.Common.Utility;
using System.Runtime.InteropServices;
using iTextSharp.text.pdf;
using System.Windows.Media;
using CapstoneProjectAEReader.Entities.Models;

namespace CapstoneProjectAEReader.Business.E_book_Reader
{
    public partial class epubReaderControl : IBook
    {
        private HTMLDocument HTMLDoc;
        private IHTMLElementCollection HrElementCollection;
        private IHTMLElement getLastPage;
        private int getTotalPage = 0;
        private string currentPage = "0";
        private string getFileLocation;
        private string progress = "Loading";
        int zoomFactor = 0;
        public XmlDocument xDoc;
        public string xmlFilePath;
        private IHTMLDocument2 IHMLDoc2;
        private IHTMLStyleSheetsCollection getStyleSheet;

        public epubReaderControl(string strFileName)
        {
            InitializeComponent();
            getFileLocation = strFileName;
            startEPUBLoading();
        }

        private void epubReader_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            HTMLDoc = (HTMLDocument)epubReader.Document.DomDocument;
            HrElementCollection = HTMLDoc.getElementsByTagName("hr");
            //Get Style Sheet
            IHMLDoc2 = (IHTMLDocument2)epubReader.Document.DomDocument;
            getStyleSheet = IHMLDoc2.styleSheets;

            zoomFactor = 100;
            progress = "Done";
        }

        public void ChangeDayNight()
        {
            HTMLDoc.body.style.color = "white";
            HTMLDoc.body.style.background = "black";
            if (getStyleSheet.item(0) != null)
            {
                IHTMLStyleSheet getStyle = getStyleSheet.item(0);
                getStyle.addRule("p", "color:white !important");
            }
        }

        public void SetFontFromSetting()
        {
            HTMLDoc.body.style.fontSize = 24;
            HTMLDoc.body.style.fontFamily = "Gabriola";
            if (getStyleSheet.item(0) != null)
            {
                IHTMLStyleSheet getStyle = getStyleSheet.item(0);
                getStyle.addRule("p", "font-size:24pt !important");
                getStyle.addRule("p", "font-family:Arial !important;");
            }
        }

        private void DoLoading(object sender, DoWorkEventArgs e)
        {
            Epub epub = new Epub(getFileLocation);
            string documentText = epub.GetContentAsHtml();
            epubReader.DocumentText = documentText;
            getLastPage = null;
            getTotalPage = epub.epubPage;

            // -- Do WTF -- ?
            string xmlFileName;
            string strTempFolder;
            string booktitle = "Unknown";
            if (epub.Title[0] != null)
            {
                booktitle = epub.Title[0];
            }
            xmlFileName = booktitle + ".xml";
            strTempFolder = "AeReaderData\\Temp";
            if (!Directory.Exists(strTempFolder))
            {
                Directory.CreateDirectory(strTempFolder);
            }
            //Create xml bookmark
            xmlFilePath = strTempFolder + "\\" + xmlFileName;
            if (!File.Exists(xmlFilePath))
            {
                //System.IO.File.WriteAllText(@xmlFilePath, "WTF?");
                ExportBookmarksToXml(getFileLocation, xmlFilePath, "");
            }
            //Create Cover
            string imageFilePath = strTempFolder + "\\" + booktitle + ".png";
            GetPdfThumbnail(getFileLocation,imageFilePath);
        }

        public override string CheckLoading()
        {
            return progress;
        }

        public void startEPUBLoading()
        {
            //create background worker
            BackgroundWorker worker = new BackgroundWorker();
            //assign it work
            worker.DoWork += new DoWorkEventHandler(DoLoading);
            //start work
            worker.RunWorkerAsync();
        }

        public override string CheckCurrentPage()
        {
            if (HrElementCollection != null)
            {
                int getCurrent_OffsetY = HTMLDoc.documentElement.getAttribute("scrollTop");
                int getCompare_OffsetY = 0;
                int compareResult = 0;
                int currentResult = int.MaxValue;
                foreach (IHTMLElement curElement in HrElementCollection)
                {
                    getCompare_OffsetY = curElement.offsetTop;
                    compareResult = getCurrent_OffsetY - getCompare_OffsetY;
                    if (compareResult >= 0 && compareResult <= currentResult)
                    {
                        currentResult = compareResult;
                        currentPage = curElement.id;
                        currentPage = currentPage.Substring(8);
                    }
                }
                return currentPage;
            } else
            {
                return "NaN";
            }
        }

        public override void DisplayNextPage()
        {
            int getNextPage = int.Parse(currentPage);
            if (HrElementCollection != null && getNextPage < getTotalPage)
            {
                getNextPage += 1;
                currentPage = getNextPage.ToString();
                string pageID = "headpage" + currentPage;
                IHTMLElement curElement = HTMLDoc.getElementById(pageID);
                if (curElement != null)
                {
                    int offSetTop = curElement.offsetTop + 10;
                    HTMLDoc.parentWindow.scrollTo(0, offSetTop);
                }
            }
        }

        /// <summary>
        /// Display previous page
        /// </summary>
        public override void DisplayPrevioudPage()
        {
            int getNextPage = int.Parse(currentPage);
            if (HrElementCollection != null && getNextPage >= 1)
            {
                getNextPage -= 1;
                currentPage = getNextPage.ToString();
                string pageID = "headpage" + currentPage;
                IHTMLElement curElement = HTMLDoc.getElementById(pageID);
                if (curElement != null)
                {
                    int offSetTop = curElement.offsetTop + 10;
                    HTMLDoc.parentWindow.scrollTo(0, offSetTop);
                }
            }
        }

        /// <summary>
        /// Display first page
        /// </summary>
        public override void DisplayFirstPage()
        {
            if (HrElementCollection != null) { 
                foreach (IHTMLElement curElement in HrElementCollection)
                {
                    string getPageID = curElement.getAttribute("id");
                    if (getPageID == "headpage1")
                    {
                        int offSetTop = curElement.offsetTop + 10;
                        HTMLDoc.parentWindow.scrollTo(0, offSetTop);
                        currentPage = "0";
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Display last page
        /// </summary>
        public override void DisplayLastPage()
        {
            if (getLastPage == null)
            {
                string lastPageString = "headpage" + getTotalPage;
                foreach (IHTMLElement curElement in HrElementCollection)
                {
                    string getPageID = curElement.getAttribute("id");
                    if (getPageID == lastPageString)
                    {
                        getLastPage = curElement;
                        int offSetTop = getLastPage.offsetTop + 10;
                        HTMLDoc.parentWindow.scrollTo(0, offSetTop);
                        currentPage = getTotalPage.ToString();
                        break;
                    }
                }
            }
            else
            {
                int offSetTop = getLastPage.offsetTop + 10;
                HTMLDoc.parentWindow.scrollTo(0, offSetTop);
                currentPage = getTotalPage.ToString();
            }
        }

        /// <summary>
        /// Zoom in
        /// </summary>
        public override void ZoomIn()
        {
            if (zoomFactor <= 280)
            {
                zoomFactor += 20;
                ((SHDocVw.WebBrowser)epubReader.ActiveXInstance).ExecWB(SHDocVw.OLECMDID.OLECMDID_OPTICAL_ZOOM,
                SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, zoomFactor, IntPtr.Zero);
            }
            else
            {
                System.Windows.MessageBox.Show("Maximum Zoom Out!!!");
            }
            
        }

        /// <summary>
        /// Zoom out
        /// </summary>
        public override void ZoomOut()
        {
            if (zoomFactor>= 20)
            {
                zoomFactor -= 20;
                ((SHDocVw.WebBrowser)epubReader.ActiveXInstance).ExecWB(SHDocVw.OLECMDID.OLECMDID_OPTICAL_ZOOM,
                SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, zoomFactor, IntPtr.Zero);
            } else
            {
                System.Windows.MessageBox.Show("Minimum Zoom Out !!!");
            }
        }

        /// <summary>
        /// Go to specific page
        /// </summary>
        /// <param name="pageNumber">Desired Page Number</param>
        public override void GoToPage(int pageNumber)
        {
            try
            {
                int getPage = pageNumber;
                if (HrElementCollection != null && getPage >= 0 && getPage <= getTotalPage)
                {
                    currentPage = getPage.ToString();
                    string pageID = "headpage" + currentPage;
                    IHTMLElement curElement = HTMLDoc.getElementById(pageID);
                    if (curElement != null)
                    {
                        int offSetTop = curElement.offsetTop + 10;
                        HTMLDoc.parentWindow.scrollTo(0, offSetTop);
                    }
                }
                else
                {
                    System.Windows.MessageBox.Show("Page doesn't exist !!!");
                }
            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show("Please input positive integer !!!");
            }
        }

        /// <summary>
        /// Open Note Form
        /// </summary>
        /// <param name="selectedContent">selected content</param>
        public override string GetSelectedText()
        {
            string getText = null;
            IHTMLDocument2 htmlDocument = epubReader.Document.DomDocument as IHTMLDocument2;
            IHTMLSelectionObject currentSelection = htmlDocument.selection;

            if (currentSelection != null)
            {
                IHTMLTxtRange range = currentSelection.createRange() as IHTMLTxtRange;

                if (range != null)
                {
                    getText = range.text;
                }
            }
            else
            {
                System.Windows.MessageBox.Show("Please select text first !!");
            }
            //System.Diagnostics.Debug.WriteLine("in");
            return getText;
        }

        /// <summary>
        /// Add new bookmark
        /// </summary>
        /// <param name="tempBm">new bookmark</param>
        public override void AddBookmark(RoutedPropertyChangedEventArgs<object> e, string strSelectedItem, string strAddingItem, string strPageNumber)
        {
            string strBookmarkPageNumber = "";
            string strNodePath = "/Bookmark/";
            Stack<TreeViewItem> path = GetNodeParent(e.NewValue as UIElement);
            foreach (var treeViewItem in path)
            {
                strNodePath += "Title" + "/";
            }
            if (strNodePath.Length > 1)
            {
                //strNodePath += strSelectedItem.Substring(0, strSelectedItem.Length -1);
            }

            strNodePath = strNodePath.Substring(0, strNodePath.Length - 1);


            //strNodePath = "/Bookmark/Title/Title[contains(text(),'Errata')]";
            strNodePath += "[contains(text(),'" + strSelectedItem.Trim() + "')]";
            XmlNode node = xDoc.SelectSingleNode(strNodePath);
            if (node != null)
            {
                XmlElement childElement = xDoc.CreateElement("Title");
                childElement.InnerText = strAddingItem;
                childElement.SetAttribute("Page", strPageNumber + " ");
                childElement.SetAttribute("Action", "GoTo");
                node.ParentNode.AppendChild(childElement);
            }
            xDoc.Save(xmlFilePath);
        }

        /// <summary>
        /// Delete bookmark
        /// </summary>
        /// <param name="tempBm"></param>
        public override void DeleteBookmark(RoutedPropertyChangedEventArgs<object> e, string strSelectedItem) {
            string strBookmarkPageNumber = "";
            string strNodePath = "/Bookmark/Title/";
            Stack<TreeViewItem> path = GetNodeParent(e.NewValue as UIElement);
            foreach (var treeViewItem in path)
            {
                strNodePath += "Title" + "/";
            }
            if (strNodePath.Length > 1)
            {
                //strNodePath += strSelectedItem.Substring(0, strSelectedItem.Length -1);
            }

            strNodePath = strNodePath.Substring(0, strNodePath.Length - 1);


            //strNodePath = "/Bookmark/Title/Title[contains(text(),'Errata')]";
            strNodePath += "[contains(text(),'" + strSelectedItem.Trim() + "')]";
            XmlNode node = xDoc.SelectSingleNode(strNodePath);
            if (node != null)
            {
                node.ParentNode.RemoveChild(node);
            }
            xDoc.Save(xmlFilePath);
        }

        /// <summary>
        /// Update bookmark
        /// </summary>
        /// <param name="tempBm"></param>
        public override void UpdateBookmark(RoutedPropertyChangedEventArgs<object> e, string strSelectedItem, string strUpdateItem)
        {
            string strBookmarkPageNumber = "";
            string strNodePath = "/Bookmark/";
            Stack<TreeViewItem> path = GetNodeParent(e.NewValue as UIElement);
            foreach (var treeViewItem in path)
            {
                strNodePath += "Title" + "/";
            }
            if (strNodePath.Length > 1)
            {
                //strNodePath += strSelectedItem.Substring(0, strSelectedItem.Length -1);
            }

            strNodePath = strNodePath.Substring(0, strNodePath.Length - 1);


            //strNodePath = "/Bookmark/Title/Title[contains(text(),'Errata')]";
            strNodePath += "[contains(text(),'" + strSelectedItem.Trim() + "')]";
            XmlNode node = xDoc.SelectSingleNode(strNodePath);
            if (node != null)
            {
                node.InnerText = strUpdateItem;
            }
            xDoc.Save(xmlFilePath);
        }

        /// <summary>
        /// Search for content
        /// </summary>
        /// <param name="content">desired texts</param>
        [DllImport("user32.dll", SetLastError = true)]
        static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);

        public const int VK_LCONTROL = 0xA2; //Left Control key code
        public const int F = 0x46; //A Control key code

        public override void SearchContent(string content)
        {
            this.epubReader.Focus();
            keybd_event(VK_LCONTROL, 0, 0, 0);
            keybd_event(F, 0, 0, 0);
            keybd_event(F, 0, 2, 0);
            keybd_event(VK_LCONTROL, 0, 2, 0);
        }

        /// <summary>
        /// Load Bookmark
        /// </summary>
        /// <returns></returns>
        public override void LoadBookmark(System.Windows.Controls.TreeView tvBookmark, string strXmlFilePath)
        {
            try
            {
                xDoc = new XmlDocument();
                xDoc.Load(strXmlFilePath);

                tvBookmark.Items.Clear();
                TreeViewItem nodeToAdd = new TreeViewItem();
                nodeToAdd.Header = xDoc.DocumentElement.Name;
                tvBookmark.Items.Add(nodeToAdd);
                TreeViewItem tNode = new TreeViewItem();
                tNode = (TreeViewItem)tvBookmark.Items[0];

                addTreeNode(xDoc.DocumentElement, tNode);
                foreach (TreeViewItem item in tvBookmark.Items)
                {
                    item.IsExpanded = true;
                }
            }
            catch (Exception)
            {
                //MessageBox.Show("a");
            }
        }

        /// <summary>
        /// Dispose Control
        /// </summary>
        public override void DisposeControl() { }

        public override void DisposeControl(bool disposing) { }

        public override string GetBookmarkPageNumber(RoutedPropertyChangedEventArgs<object> e, string strSelectedItem)
        {
            string strBookmarkPageNumber = "";
            string strNodePath = "/Bookmark/";
            Stack<TreeViewItem> path = GetNodeParent(e.NewValue as UIElement);
            foreach (var treeViewItem in path)
            {
                strNodePath += "Title" + "/";
            }

            strNodePath = strNodePath.Substring(0, strNodePath.Length - 1);

            strNodePath += "[contains(text(),'" + strSelectedItem.Trim() + "')]";

            XmlNode node = xDoc.SelectSingleNode(strNodePath);
            if (node != null)
            {
                XmlNode node2 = node.Attributes["Page"];
                if (node2 != null)
                {
                    string strPageNumber = node2.Value;
                    strPageNumber = strPageNumber.Substring(0, strPageNumber.IndexOf(" "));
                    if (strPageNumber != null)
                    {
                        strBookmarkPageNumber = strPageNumber;
                    }
                }

            }
            return strBookmarkPageNumber;
        }

        public static void ExportBookmarksToXml(string SourcePdfPath, string xmlOutputPath, string Password = "")
        {
            try
            {
                PdfReader reader = new PdfReader(SourcePdfPath, new System.Text.ASCIIEncoding().GetBytes(Password));
                IList<Dictionary<string, object>> bookmarks = SimpleBookmark.GetBookmark(reader);
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    SimpleBookmark.ExportToXML(bookmarks, memoryStream, "ISO8859-1", true);
                    //MessageBox.Show(bookmarks[0].Values.ToString());
                    File.WriteAllBytes(xmlOutputPath, memoryStream.ToArray());
                }
            }
            catch (Exception)
            {
                XmlDocument XD = new XmlDocument();
                XmlNode Root = XD.AppendChild(XD.CreateElement("Bookmark"));
                XmlElement childElement = XD.CreateElement("Title");
                childElement.InnerText = "First Page";
                childElement.SetAttribute("Page", "1 ");
                childElement.SetAttribute("Action", "GoTo");
                Root.AppendChild(childElement);
                /*childElement = XD.CreateElement("Title");
                childElement.InnerText = "Last Page";
                childElement.SetAttribute("Page", "100000 ");
                childElement.SetAttribute("Action", "GoTo");
                Root.AppendChild(childElement);*/
                XmlDeclaration xmldecl;
                xmldecl = XD.CreateXmlDeclaration("1.0", null, null);
                xmldecl.Encoding = "ISO-8859-1";
                xmldecl.Standalone = "yes";
                XD.Save(xmlOutputPath);
            }
        }

        private void addTreeNode(XmlNode xmlNode, TreeViewItem treeNode)
        {
            XmlNode xNode;
            TreeViewItem tNode;
            XmlNodeList xNodeList;
            string strInnerText;
            string strPageNumber;
            string strXPath;
            if (xmlNode.HasChildNodes) //The current node has children
            {
                xNodeList = xmlNode.ChildNodes;
                if (xNodeList.Count > 1)
                {
                    if (xmlNode.Name.Equals("Bookmark"))
                    {
                        for (int x = 0; x <= xNodeList.Count - 1; x++)
                        //Loop through the child nodes
                        {
                            xNode = xmlNode.ChildNodes[x];
                            strInnerText = xNode.InnerText;
                            if (strInnerText.Contains("\n"))
                            {
                                strInnerText = xNode.InnerText.Substring(0, xNode.InnerText.IndexOf("\n"));
                            }

                            treeNode.Items.Add(new TreeViewItem() { Header = strInnerText.Trim() });

                            tNode = treeNode.Items[x] as TreeViewItem;
                            addTreeNode(xNode, tNode);


                        }
                    }
                    else
                    {
                        for (int x = 1; x <= xNodeList.Count - 1; x++)
                        //Loop through the child nodes
                        {
                            xNode = xmlNode.ChildNodes[x];
                            strInnerText = xNode.InnerText;
                            if (strInnerText.Contains("\n"))
                            {
                                strInnerText = xNode.InnerText.Substring(0, xNode.InnerText.IndexOf("\n"));
                            }

                            treeNode.Items.Add(new TreeViewItem() { Header = strInnerText.Trim() });

                            tNode = treeNode.Items[x - 1] as TreeViewItem;
                            addTreeNode(xNode, tNode);


                        }
                    }
                }
                else
                {
                    xNode = xmlNode.ChildNodes[0];
                    treeNode.Header = xNode.InnerText.Trim();

                }

            }
            else
            {
                //No children, so add the outer xml (trimming off whitespace)         
                treeNode.Header = xmlNode.OuterXml.Trim();

            }


        }


        public static Stack<TreeViewItem> GetNodeParent(UIElement element)
        {

            Stack<TreeViewItem> tempNodePath = new Stack<TreeViewItem>();
            // Walk up the element tree to the nearest tree view item. 
            TreeViewItem container = element as TreeViewItem;


            while ((element != null))
            {

                element = VisualTreeHelper.GetParent(element) as UIElement;
                container = element as TreeViewItem;
                if (container != null)
                    tempNodePath.Push(container);
            }

            return tempNodePath;
        }

        public override Ebook GenerateEbookObj(string filePath)
        {
            Ebook myEbook = new Ebook();
            //File type
            FileInfo temp = new FileInfo(filePath);
            if (temp.Extension.Equals(".epub", StringComparison.InvariantCultureIgnoreCase))
            {
                myEbook.Type = ".epub";
            }
            //Create Ebook MetaData

            Epub getEpubBook = new Epub(filePath);
            string activePageCount = getEpubBook.GetContentAsHtml();
            myEbook.Title = Path.GetFileNameWithoutExtension(filePath);
            //if (getEpubBook.Title[0] != null)
            //{
            //    myEbook.Title = getEpubBook.Title[0];
            //}
            if (getEpubBook.Creator[0] != null)
            {
                myEbook.Author = getEpubBook.Creator[0];
            }
            if (getEpubBook.Publisher[0] != null)
            {
                myEbook.Publisher = getEpubBook.Publisher[0];
            }
            myEbook.Path = filePath;
            myEbook.ImportDate = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
            myEbook.Rating = 0;
            myEbook.CurrentPage = 0;
            myEbook.BookmarkPath = "AeReaderData\\Temp\\" + myEbook.Title + ".xml";
            myEbook.PageCount = getEpubBook.epubPage;

            return myEbook;
        }

        public override void GetPdfThumbnail(string sourcePdfFilePath, string destinationPngFilePath)
        {
            Epub epub = new Epub(sourcePdfFilePath);

            if (!File.Exists(destinationPngFilePath))
            {
                byte[] imageBytes = Convert.FromBase64String(epub.coverBase64);
                MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);
                ms.Write(imageBytes, 0, imageBytes.Length);
                System.Drawing.Image image = System.Drawing.Image.FromStream(ms, true);
                System.Drawing.Image newImage = image.GetThumbnailImage(300, 400, null, IntPtr.Zero);
                newImage.Save(@destinationPngFilePath, System.Drawing.Imaging.ImageFormat.Png);
            }
        }

        public override void SetReadingMode(int mode) 
        {
            HTMLDoc.body.style.color = "white";
            HTMLDoc.body.style.background = "black";
            if (getStyleSheet.item(0) != null)
            {
                IHTMLStyleSheet getStyle = getStyleSheet.item(0);
                getStyle.addRule("p", "color:white !important");
            }
        }
        public override void SetFontSize(int size) 
        {
            HTMLDoc.body.style.fontSize = size;
            if (getStyleSheet.item(0) != null)
            {
                IHTMLStyleSheet getStyle = getStyleSheet.item(0);
                getStyle.addRule("p", String.Format("font-size:{0}pt !important"),size);
            }
        }
        public override void SetFontType(string type) 
        {
            HTMLDoc.body.style.fontFamily = type;
            if (getStyleSheet.item(0) != null)
            {
                IHTMLStyleSheet getStyle = getStyleSheet.item(0);                
                getStyle.addRule("p", String.Format("font-family:{0} !important;",type));
            }
        }


        //---------------------End---------------------
    }
}
